package com.lianda.java8.functional.base;

import org.apache.commons.lang3.reflect.MethodUtils;

import java.lang.invoke.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * 用函数式编程替代反射测试
 * https://www.cnblogs.com/blouson/p/11258770.html
 * https://www.jianshu.com/p/8dc46a2dc21d
 */
public class ReflectFunctionalMain {
    static String[] arr = { "str01", "str02", "str03", "str04", "str05", "str06", "str07", "str08", "str09", "str10"};
    /**
     * BiConsumer<T, U>
     *  T为第一个参数类型，U为第二个参数类型；
     * 输入参数2个，无返回输出结果；
     */
    //accept方法列表
    static List<BiConsumer<Item, String>> biConsumers = Arrays.asList(Item::setVar1, Item::setVar2, Item::setVar3,
            Item::setVar4, Item::setVar5, Item::setVar6, Item::setVar7, Item::setVar8, Item::setVar9, Item::setVar10);

    static Map<String, Method> methodMap = new HashMap<>();

    static Class clazz = null;

    static Map<String, BiConsumer<Item, String>> funcMap = new HashMap<>();

    public static void main(String[] args) throws Exception {
        int i = 0;
        //反射方法测试
        long start = System.currentTimeMillis();
        for (i = 0; i < 1000000; i++) {
            testReflect();
        }
        long end = System.currentTimeMillis();
        System.out.println("反射调用执行了 " + i + " 次， 耗时" + (end -start) + "ms");

        System.out.println("====================");

        long start2 = System.currentTimeMillis();
        for (i = 0; i < 1000000; i++) {
            testCachedReflect();
        }
        long end2 = System.currentTimeMillis();
        System.out.println("缓存式反射调用执行了 " + i + " 次， 耗时" + (end2 - start2) + "ms");

        System.out.println("====================");

        long start3 = System.currentTimeMillis();
        for (i = 0; i < 1000000; i++) {
            testFunctionalMethod();
        }
        long end3 = System.currentTimeMillis();
        System.out.println("函数式方法调用执行了 " + i + " 次， 耗时" + (end3 -start3) + "ms");

        System.out.println("====================");

        long start4 = System.currentTimeMillis();
        for (i = 0; i < 1000000; i++) {
            testCachedFunctionalMethod();
        }
        long end4 = System.currentTimeMillis();
        System.out.println("缓存函数式方法调用执行了 " + i + " 次， 耗时" + (end4 -start4) + "ms");


    }

    //反射测试方法
    static void testReflect() {
        Item item = new Item();
        try {
            Class<?> clazz = Class.forName("com.lianda.java8.functional.base.Item");
            for (int i = 0; i < arr.length; i++) {
                Method method = clazz.getMethod("setVar" + (i + 1), String.class);
                method.invoke(item, arr[i]);
            }
//            System.out.println(item.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void testCachedReflect() {
        Item item = new Item();
        try {
            if (clazz == null) {
                clazz = Class.forName("com.lianda.java8.functional.base.Item");
            }
            for (int i = 0; i < arr.length; i++) {
                String methodName = "setVar" + (i + 1);
                Method method = null;
                if (!methodMap.containsKey(methodName)) {
                    method = clazz.getMethod(methodName, String.class);
                    methodMap.put(methodName, method);
                } else {
                    method = methodMap.get(methodName);
                }
                method.invoke(item, arr[i]);
            }
//            System.out.println(item.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //函数式方法
    static void testFunctionalMethod() {
        Item item = new Item();
        for (int i = 0; i < arr.length; i++) {
            biConsumers.get(i).accept(item, arr[i]);
        }
//        System.out.println(item.toString());
    }

    //函数式方法
    static void testCachedFunctionalMethod() {
        Item item = new Item();
        for (int i = 0; i < arr.length; i++) {
            String varKey = "var"+(i+1);
            BiConsumer<Item, String> func;
            func = funcMap.get("var"+(i+1));
            if (func != null) {
                func.accept(item, arr[i]);
            } else {
                func = biConsumers.get(i);
                funcMap.put(varKey, func);
                func.accept(item, arr[i]);
            }
        }
//        System.out.println(item.toString());
    }

    static void preBuildFunction() {
        Item item = new Item();
        for (int i = 0; i < arr.length; i++) {
            Method method = MethodUtils.getAccessibleMethod(Item.class, "setVar" + (i+1), String.class);
            try {
                funcMap.put("var" + (i+1), createHandlerLambda(item, method));
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    static BiConsumer createHandlerLambda(Object bean, Method method) throws Throwable {
        MethodHandles.Lookup caller = MethodHandles.lookup();
        String invokedName = "accept";
        //调用的函数式类的类型
        MethodType invokedType = MethodType.methodType(BiConsumer.class);

        //调用的形式方法的入参和出参，组成类型
        MethodType samMethodType = MethodType.methodType(void.class, Object.class, Object.class);

        MethodHandle implMethod = caller.findVirtual(bean.getClass(), method.getName(),
                MethodType.methodType(void.class, method.getParameterTypes()[0]));

        //实际调用的方法的入参和出参，组成类型
        MethodType instantiatedMethodType = MethodType.methodType(void.class, bean.getClass(),
                method.getParameterTypes()[0]);

        CallSite site = LambdaMetafactory.metafactory(
                caller, invokedName,
                invokedType,
                samMethodType,
                implMethod,
                instantiatedMethodType
        );

        MethodHandle factory = site.getTarget();
        BiConsumer listenerMethod = (BiConsumer) factory.invoke();

        return listenerMethod;
    }
}



